iT邦幫忙

2024 iThome 鐵人賽

DAY 6
0
Mobile Development

用Spring Boot架設後端結合Android前端建構智慧個人化推薦系統系列 第 6

Day6 使用Spring Boot進行單元測試:測試API與服務的最佳實踐

  • 分享至 

  • xImage
  •  

在現代軟體開發中,測試是確保程式碼品質和降低錯誤率的重要環節。對於使用Spring Boot開發的應用程式,進行單元測試能夠提升信心,確保API和服務正常運行。本文將介紹如何使用Spring Boot進行單元測試,包括測試API和服務層的最佳實踐。

什麼是單元測試?

單元測試是對應用程式中最小可測試單元的獨立檢驗。其目的是檢查該單元的正確性,以確保其如預期工作。在Spring Boot中,單元測試主要用到Junit和Mockito等框架。

Spring Boot測試的設置

  • 添加依賴

如果使用Maven,確保 pom.xml 中包含以下依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

如果使用Gradle,則在 build.gradle 中添加:

testImplementation 'org.springframework.boot:spring-boot-starter-test'
  • 測試專案結構
    假設有一個簡單的Spring Boot應用,以下是一般的專案結構:
my-spring-boot-app
│
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── demo
│                       ├── UserControllerTest.java
│                       └── UserServiceTest.java
│
├── pom.xml

測試API

假設我們有一個簡單的用戶控制器 (UserController):

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.getUserById(id)
                .map(user -> ResponseEntity.ok().body(user))
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.createUser(user);
        return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
    }
}

在 UserControllerTest.java 中,我們將測試這個控制器的功能

public class UserControllerTest {

    private MockMvc mockMvc;

    @Mock
    private UserService userService;

    @InjectMocks
    private UserController userController;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
    }

    @Test
    public void testGetUserById() throws Exception {
        User user = new User(1L, "John Doe", "john.doe@example.com");
        when(userService.getUserById(1L)).thenReturn(Optional.of(user));

        RequestBuilder request = get("/api/users/1")
                .accept(MediaType.APPLICATION_JSON);
        
        MvcResult result = mockMvc.perform(request)
                .andExpect(status().isOk())
                .andReturn();

        String expectedResponse = new ObjectMapper().writeValueAsString(user);
        assertEquals(expectedResponse, result.getResponse().getContentAsString());
    }

    @Test
    public void testCreateUser() throws Exception {
        User user = new User(null, "Jane Doe", "jane.doe@example.com");
        User savedUser = new User(1L, "Jane Doe", "jane.doe@example.com");
        when(userService.createUser(any(User.class))).thenReturn(savedUser);

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(new ObjectMapper().writeValueAsString(user)))
                .andExpect(status().isCreated());
        
        verify(userService, times(1)).createUser(any(User.class));
    }
}
  • MockMvc: 用於模擬HTTP請求,方便測試控制器。
  • Mockito: 用於模擬UserService,不需要進行實際的服務調用。
  • @BeforeEach: 在每個測試前初始化Mock和MockMvc。
  • 測試方法: testGetUserById和testCreateUser分別測試GET和POST請求的功能。

接下來,我們將測試用戶服務 (UserService)

假設UserService如下所示:

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;

    public Optional<User> getUserById(Long id) {
        return userRepository.findById(id);
    }

    public User createUser(User user) {
        return userRepository.save(user);
    }
}

在 UserServiceTest.java 中,測試UserService的功能

public class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testGetUserById() {
        User user = new User(1L, "John Doe", "john.doe@example.com");
        when(userRepository.findById(1L)).thenReturn(Optional.of(user));
        
        Optional<User> foundUser = userService.getUserById(1L);

        assertEquals("John Doe", foundUser.get().getName());
    }

    @Test
    public void testCreateUser() {
        User user = new User(null, "Jane Doe", "jane.doe@example.com");
        User savedUser = new User(1L, "Jane Doe", "jane.doe@example.com");
        when(userRepository.save(any(User.class))).thenReturn(savedUser);

        User result = userService.createUser(user);

        assertEquals("Jane Doe", result.getName());
    }
}
  • Mocking: 使用Mockito來模擬UserRepository的行為。
  • InjectMocks: 將模擬的Repository注入到UserService中。
  • 測試功能: testGetUserById測試是否能正確獲取用戶,testCreateUser測試用戶是否能正確創建。

單元測試是Spring Boot開發過程中的重要組成部分。使用Junit和Mockito來測試API和服務層的程式碼不僅能提高程式碼品質,還能在日後維護和擴展時提供信心。透過遵循上述最佳實踐,可以構建出可靠而穩定的Spring Boot應用程式


上一篇
Day5 Spring Boot中的依賴管理:解釋Maven與Gradle的選擇與使用
下一篇
Day7 Spring Boot中的配置管理:如何使用application.properties/yml檔進行配置
系列文
用Spring Boot架設後端結合Android前端建構智慧個人化推薦系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言